#include "SpiCommunicator.h"

#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include <cstring>

#include "Log.h"
#include "ErrorCode.h"
#include "SpiChannel.h"

SpiCommunicator::~SpiCommunicator() {
    //dtor
}


bool SpiCommunicator::initialize(uint8_t mode, uint8_t bits, uint32_t speed) {
    /*
     * spi mode
     */
    int ret = ioctl(deviceFd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1) {
        LOG_ERROR("can't set spi mode");
    }

    ret = ioctl(deviceFd, SPI_IOC_RD_MODE, &mode);
    if (ret == -1) {
        LOG_ERROR("can't get spi mode");
    }

    /*
     * bits per word
     */
    ret = ioctl(deviceFd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1) {
        LOG_ERROR("can't set bits per word");
    }

    ret = ioctl(deviceFd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    if (0 > ret) {
        LOG_ERROR("can't get bits per word");
    }

    /*
     * max speed hz
     */
    ret = ioctl(deviceFd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (0 > ret) {
        LOG_ERROR("can't set wr max speed hz");
    }

    ret = ioctl(deviceFd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (0 > ret) {
        LOG_ERROR("can't set rd max speed hz");
    }

    return true;
}

ssize_t SpiCommunicator::write1(const std::string& exceptionTip, void const * const data, const size_t size) noexcept {
    char buffer[MAX_BUFFER_SIZE];
    memset(buffer, 0, sizeof (buffer));
    ssize_t written = 0;
    ssize_t readed = 0;

    for (size_t i = 0; i < size; ++i) {
        written = ::write(deviceFd, (char*)data + i, 1);
        readed = ::read(deviceFd, buffer + i, 1);
    }

    std::ostringstream oss;
    oss << "send:";
    oss.write((char*)data, size);
    oss << ",feedback:" << buffer << ",readed:" << readed << std::endl;
    std::cout<<oss.str();
    return written;
}


ssize_t SpiCommunicator::read0(const std::string& exceptionTip, void * buffer, const size_t length) noexcept {
    char* pDataBuffer = reinterpret_cast <char*>(buffer);
//    usleep(1000 * 1000);
    std::memset(buffer, 0, length);

    ssize_t ret = -1;
    ssize_t readed = 0;
    const std::size_t size = 1;
    std::string request(size, '?');
    while (readed < length) {
        ::write(deviceFd, request.data(), size);

        //usleep(10 * 1000);
        ret = ::read(deviceFd, pDataBuffer + readed, size);

//        printf("spi read:ret:%zd,readed:%d,char:[%d],", ret, readed, pDataBuffer[readed]);
        if (request[0] == pDataBuffer[readed] || 0 > ret) {
            if (0 == readed) {
                readed = -1;
            }
            break;
        }

        ++readed;
        // std::string message("read error info:");
        // message.append(reinterpret_cast <char*>(buffer), 200);
//		LOG_ERROR(buildErrorMessage(ret, errno, message).data());
    }
    printf("spi read:ret:%zd,readed:%d,data:[%s]\n", ret, readed, (const char*)buffer);

    return readed;
}

ssize_t SpiCommunicator::read(const std::string& exceptionTip, void * buffer, const size_t length) noexcept {
    LOG_DEDUG("begin of transfer,length:%zu", length);
//    std::string sendForRead(length, '?');
    uint8_t sendForRead[length];
    memset(sendForRead, '?', sizeof (sendForRead));
    struct spi_ioc_transfer transfer = {
        .tx_buf = reinterpret_cast <unsigned long>(sendForRead),
        .rx_buf = reinterpret_cast <unsigned long>(buffer),
        //.len = ARRAY_SIZE(tx),
        .len = length,
//        .speed_hz = SPEED,
//        .delay_usecs = 0,
//        .bits_per_word = 8,
    };
//    usleep(1000 * 1000);
    int ret = ioctl(deviceFd, SPI_IOC_MESSAGE(1), &transfer);

    LOG_DEDUG("end of transfer");

    ssize_t readed = ret;
    if (0 > ret) {
    } else {
        const char* szdata = reinterpret_cast <const char*>(buffer);
        const void* p = memchr(szdata + SpiChannel::DATA_OFFSET, '>', length);
        if (nullptr != p){
            readed = reinterpret_cast <const char*>(p) - szdata + 1;
        } else {
            readed = length;
        }
    }

    std::string message("error info:");
    buildErrorMessage(ret, errno, message);
    message.append(",recv:");
    const size_t debugSize = std::min(DEBUG_OUTPUT_SIZE, (size_t)readed);
    StringUtility::dumpMemory(reinterpret_cast <uint8_t*>(buffer), debugSize, "%u,", message);
    message.append(",string:").append((const char*)buffer, debugSize);
    message.append(",readed:");
    std::cout << message << readed << std::endl;

    return readed;
}



